package com.yjq.blog.article.service.impl;

import com.alibaba.fastjson.JSON;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.yjq.blog.article.consts.ArticleConsts;
import com.yjq.blog.article.dao.ArticleDao;
import com.yjq.blog.article.dao.ArticleTagDao;
import com.yjq.blog.article.dao.ArticleTypeDao;
import com.yjq.blog.article.dto.ArticleSearchDTO;
import com.yjq.blog.article.dto.ArticleTagSearchDTO;
import com.yjq.blog.article.entity.Article;
import com.yjq.blog.article.entity.ArticleTag;
import com.yjq.blog.article.entity.ArticleType;
import com.yjq.blog.article.entity.User;
import com.yjq.blog.article.feign.UserClientFeign;
import com.yjq.blog.article.service.ArticleService;
import com.yjq.blog.article.vo.ArticleAddVO;
import com.yjq.blog.article.vo.ArticleListResponseVO;
import com.yjq.blog.article.vo.ArticleSearchVO;
import com.yjq.blog.article.vo.ArticleUpdateVO;
import com.yjq.blog.utils.Page;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
public class ArticleServiceImpl implements ArticleService {

    @Resource
    private ArticleDao articleDao;

    @Resource
    private ArticleTypeDao articleTypeDao;

    @Resource
    private ArticleTagDao articleTagDao;

    @Resource
    private UserClientFeign userClientFeign;

    @Override
    public Page<ArticleListResponseVO> pageArticle (ArticleSearchVO searchVO, Integer pageNo, Integer pageSize) {

        // 查询所有类型, 获取查询DTO
        List<ArticleType> allTypeList = articleTypeDao.listArticleType();
        ArticleSearchDTO searchDTO = getArticleSearchDTO(searchVO, allTypeList);

        // 根据条件查询文章
        PageHelper.startPage(pageNo, pageSize);
        List<Article> list = articleDao.listArticle(searchDTO);
        PageInfo<Article> pageInfo = new PageInfo<>(list);

        List<ArticleListResponseVO> resultList = new ArrayList<>(pageSize);
        BeanCopier beanCopier = BeanCopier.create(Article.class, ArticleListResponseVO.class, false);
        Map<Integer, ArticleType> articleTypeMap = allTypeList.stream().collect(Collectors.toMap(ArticleType::getId, Function.identity()));
        Map<Integer, Integer> parentIdMap = allTypeList.stream().collect(Collectors.toMap(ArticleType::getId, ArticleType::getParentId));
        Set<Integer> userIdSet = list.stream().map(Article::getUserId).collect(Collectors.toSet());
        Map<Integer, User> userMap = userClientFeign.getUserMapByIdList(new ArrayList<>(userIdSet));

        // 填充文章类型名称及作者名称
        list.forEach(article -> {
            ArticleListResponseVO result = new ArticleListResponseVO();
            beanCopier.copy(article, result, null);
            Integer tempId = article.getTypeId();
            List<ArticleListResponseVO.Type> tempList = new ArrayList<>();
            while (tempId != null && tempId > 0) {
                ArticleType tempType = articleTypeMap.get(tempId);
                if(tempType != null) {
                    tempList.add(new ArticleListResponseVO.Type(tempType.getId(), tempType.getTitle()));
                }
                tempId = parentIdMap.get(tempId);
            }
            Collections.reverse(tempList);
            result.setTypeList(tempList);

            // Response<User> response = userClientFeign.getUserMapByIdList(article.getUserId());
            // User user = Optional.ofNullable(response).flatMap(res -> Optional.ofNullable(res.getData())).orElse(new User());
            User user = Optional.ofNullable(userMap.get(article.getUserId())).orElse(new User());
            result.setUserName(user.getNickname());
            resultList.add(result);
        });

        return new Page<>(resultList, pageNo, pageSize, pageInfo.getPages(), pageInfo.getTotal());
    }

    @Override
    public ArticleAddVO getArticleById(Integer id) {
        ArticleAddVO result = new ArticleAddVO();
        Article article = articleDao.getArticleById(id);
        BeanUtils.copyProperties(article, result);

        List<ArticleTag> tagList = articleTagDao.listArticleTag(ArticleTagSearchDTO.builder().articleId(id).build());
        result.setTagNameList(!CollectionUtils.isEmpty(tagList)
                ? tagList.stream().map(ArticleTag::getTagName).collect(Collectors.toList())
                : new ArrayList<>());

        result.setRefWebUrlList(StringUtils.isNoneBlank(article.getRefWeb())
                ? List.of(article.getRefWeb().split(ArticleConsts.webUrlRegular))
                : new ArrayList<>());
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean addArticle(ArticleAddVO articleVO, Integer userId) {

        Article article = new Article();
        article.setInner(articleVO.getInner());
        article.setCreateTime(new Date());
        article.setCreateUserId(userId);
        article.setIsDeleted(false);
        article.setLevel(articleVO.getLevel());
        article.setProfile(articleVO.getProfile());
        article.setIsTop(articleVO.getIsTop());
        article.setRecommendLevel(articleVO.getRecommendLevel());
        article.setUpdateTime(new Date());
        article.setUpdateUserId(userId);
        article.setTypeId(articleVO.getTypeId());
        article.setTitle(articleVO.getTitle());
        article.setUserId(userId);
        article.setRefWeb(CollectionUtils.isEmpty(articleVO.getRefWebUrlList())
                ? ""
                : org.apache.commons.lang3.StringUtils.join(articleVO.getRefWebUrlList(), ArticleConsts.webUrlSeparator));

        boolean res = articleDao.addArticle(article) == 1;

        if(res && !CollectionUtils.isEmpty(articleVO.getTagNameList())) {
            ArticleTag articleTag = new ArticleTag();
            articleTag.setArticleId(article.getId());
            articleTag.setCreateTime(new Date());
            articleTag.setUpdateTime(new Date());
            articleTag.setIsDeleted(false);
            for(String tagName : articleVO.getTagNameList()) {
                articleTag.setTagName(tagName);
                articleTagDao.addArticleTag(articleTag);
            }
        }
        return res;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateArticle(ArticleUpdateVO articleVO, Integer userId) {
        // 更新文章表
        Article article = new Article();
        article.setId(articleVO.getId());
        article.setInner(articleVO.getInner());
        article.setLevel(articleVO.getLevel());
        article.setProfile(articleVO.getProfile());
        article.setIsTop(articleVO.getIsTop());
        article.setRecommendLevel(articleVO.getRecommendLevel());
        article.setUpdateTime(new Date());
        article.setUpdateUserId(userId);
        article.setTypeId(articleVO.getTypeId());
        article.setTitle(articleVO.getTitle());
        article.setRefWeb(CollectionUtils.isEmpty(articleVO.getRefWebUrlList())
                ? ""
                : org.apache.commons.lang3.StringUtils.join(articleVO.getRefWebUrlList(), ArticleConsts.webUrlSeparator));
        // 查询已有标签
        List<ArticleTag> tagList = articleTagDao.listArticleTag(ArticleTagSearchDTO.builder().articleId(articleVO.getId()).build());
        Map<String, Integer> tagMap = tagList.stream().collect(Collectors.toMap(ArticleTag::getTagName, ArticleTag::getId));
        Set<Integer> tagIdSet = tagList.stream().map(ArticleTag::getId).collect(Collectors.toSet());
        if(!CollectionUtils.isEmpty(articleVO.getTagNameList())) {
            for(String tagName : articleVO.getTagNameList()) {
                Integer id = tagMap.get(tagName);
                // 存在此标签名，则从set中移除，否则添加新标签
                if(id == null) {
                    ArticleTag articleTag = new ArticleTag();
                    articleTag.setArticleId(articleVO.getId());
                    articleTag.setTagName(tagName);
                    articleTag.setIsDeleted(false);
                    articleTag.setCreateTime(new Date());
                    articleTag.setUpdateTime(new Date());
                    articleTagDao.addArticleTag(articleTag);
                } else {
                    tagIdSet.remove(id);
                }
            }
        }
        // 将set中剩余标签id删除
        for(Integer id : tagIdSet) {
            ArticleTag articleTag = new ArticleTag();
            articleTag.setId(id);
            articleTag.setUpdateTime(new Date());
            articleTag.setIsDeleted(true);
            articleTagDao.updateByPrimaryKeySelective(articleTag);
        }

        return articleDao.updateArticle(article) == 1;
    }

    private ArticleSearchDTO getArticleSearchDTO (ArticleSearchVO searchVO, List<ArticleType> allTypeList) {
        ArticleSearchDTO searchDTO = ArticleSearchDTO.builder()
                .articleTitle(searchVO.getArticleTitle())
                .isDeleted(searchVO.getIsDeleted())
                .build();
        // 如果查询包含子集，则查询本类型下的所有子集
        if(!CollectionUtils.isEmpty(searchVO.getTypeList())) {
            if(BooleanUtils.isTrue(searchVO.getContainChildType())) {
                List<Integer> typeIdList = new ArrayList<>();
                for(Integer typeId : searchVO.getTypeList()) {
                    typeIdList.addAll(getChildrenId(typeId, allTypeList, true));
                }
                searchDTO.setTypeIdList(typeIdList);
            } else {
                searchDTO.setTypeIdList(searchVO.getTypeList());
            }
        }
        if(StringUtils.isNoneBlank(searchVO.getTagName())) {
            List<ArticleTag> tagList = articleTagDao.listArticleTag(ArticleTagSearchDTO.builder().tagName(searchVO.getTagName()).build());
            searchDTO.setIdList(tagList.stream().map(ArticleTag::getArticleId).collect(Collectors.toList()));
        }
        return searchDTO;
    }
    private List<Integer> getChildrenId(Integer typeId, List<ArticleType> sourceList, boolean containSelf) {

        // 根据源列表获取子类型Map
        Map<Integer, List<ArticleType>> childrenMap = childrenTypeMap(sourceList);

        List<Integer> result = new ArrayList<>();
        List<ArticleType> typeList = childrenMap.get(typeId);

        // 当类型列表不为空时，添加其元素id
        while(!CollectionUtils.isEmpty(typeList)) {
            // 每次循环重置列表
            List<ArticleType> tempList = new ArrayList<>();
            for(ArticleType type : typeList) {
                result.add(type.getId());
                tempList.addAll(Optional.ofNullable(childrenMap.get(type.getId())).orElse(new ArrayList<>()));
            }
            typeList = tempList;
        }

        // 是否包含自身
        if(containSelf) {
            result.add(typeId);
        }
        return result;
    }

    private Map<Integer, List<ArticleType>> childrenTypeMap(List<ArticleType> list) {
        return list.stream().collect(Collectors.groupingBy(ArticleType::getParentId));
    }

}
